#include "TablaIndice.h"
#include <string.h>
#include <stdio.h>
#include "TerminoSimplificado.h"
#include "Delta.h"

TablaIndice::TablaIndice() {
	this->pos_actual = 0;
	this->tabla=0;
	this->entradas = 0;
	this->tamanio = 0;
}

TablaIndice::~TablaIndice() {
	if(this->tabla) {
		delete[] this->tabla;
	}
}

TablaIndice::TablaIndice(const std::string &tabla, const std::string
		&path_lexico, const std::string &path_punteros) {
	FILE* p = fopen(tabla.c_str(),"r");
	fseek(p, 0L, SEEK_END );
	int final = ftell(p);
	this->entradas = final / sizeof(tabla_indice);
	fseek(p,0L, SEEK_SET);
	this->tabla = new tabla_indice[this->entradas];
	memset(this->tabla, 0, this->entradas);

	fread(this->tabla, final, sizeof(char), p);
	fclose(p);
	this->pos_actual = 0;
	this->tamanio = final;

	this->path_lexico = path_lexico;
	this->path_punteros = path_punteros;

}

void TablaIndice::set_entradas(int cantidad) {
	this->tabla = new tabla_indice[cantidad];
	memset(this->tabla, 0, cantidad*sizeof(tabla_indice));
	this->entradas = cantidad;
	this->tamanio = (cantidad* sizeof(tabla_indice));
}

void TablaIndice::agregar_entrada(int offset_l, int offset_p, unsigned short int frec) {
	tabla_indice tabla;
	tabla.offset_lexico = offset_l;
	tabla.offset_punteros = offset_p;
	tabla.frec = frec;
	this->tabla[pos_actual] = tabla;
}

void TablaIndice::set_datos_frontcoding(unsigned short int distintos, unsigned short int iguales) {
	this->tabla[pos_actual].distintos = distintos;
	this->tabla[pos_actual].iguales = iguales;
	pos_actual++;
}

void TablaIndice::grabar(const std::string &path) {
	FILE* salida = fopen(path.c_str(),"w+");
	fwrite(this->tabla,sizeof(tabla_indice),this->entradas,salida);
	fclose(salida);
}

int TablaIndice::get_size() {
	return this->tamanio;
}

tabla_indice* TablaIndice::get_tabla() {
	return this->tabla;
}

void TablaIndice::buscar(const std::string &palabra, DocumentosRecuperados* docs, int pos_termino,
		vector_consulta* consulta) {
	FILE* lexico = fopen(this->path_lexico.c_str(),"r");

	fseek(lexico, 0L, SEEK_END );
	int final = ftell(lexico);
	char* buffer = new char[final];
	fseek(lexico,0L, SEEK_SET);

	fread(buffer,final,sizeof(char),lexico);

	int medio, piso;
	medio = piso = 0;
	int techo = this->entradas -1 ;

	techo = techo / N_FRONTCODING;
	tabla_indice actual, siguiente;
	bool end = false;
	char* ter_anterior = 0;
	char* ter_actual = 0;
	bool encontrado = false;
	int longitud = 0;
	int indice = 0;

	while (!end) {

		medio = ((techo-piso) /2) + piso;

		indice = medio * N_FRONTCODING;

		actual = this->tabla[indice];
		siguiente = this->tabla[indice+1];
		int offset_l = actual.offset_lexico;
		unsigned short int cantidad = (siguiente.offset_lexico) -(actual.offset_lexico);



		ter_actual = new char[cantidad];
		memset(ter_actual,0,cantidad);
		memcpy(ter_actual,&buffer[offset_l],cantidad);

		if (cantidad > (palabra.size())) {
			longitud = palabra.size();
		}
		else {
			longitud = cantidad;
		}

		int res = strncmp(ter_actual,palabra.c_str(), longitud);

		if (res == 0) {
			if (cantidad == palabra.size()) {
				encontrado = true;
				this->agregar_doc_rec(indice,consulta,docs,pos_termino);
			}
			else {
				if (palabra.size() > cantidad) {
				res = -1;
				}
				else {
					res = 1;
				}
			}
		}

		if (!encontrado){
			if (res < 0) {
				int k = 1;

				while ((k < N_FRONTCODING) && (!encontrado)) {
					indice++;
					ter_anterior = ter_actual;
					actual = this->tabla[indice];
					siguiente = this->tabla[indice+1];
					offset_l = actual.offset_lexico;
					cantidad = (siguiente.offset_lexico) -(actual.offset_lexico);
					ter_actual = new char[actual.distintos + actual.iguales];
					memcpy(ter_actual, ter_anterior, actual.iguales);

					memcpy(&ter_actual[actual.iguales], &buffer[offset_l], actual.distintos);
					unsigned short int tamanio_actual = actual.distintos+ actual.iguales;
					if ((tamanio_actual) > (palabra.size())) {
						longitud = palabra.size();
					}
					else {
						longitud = tamanio_actual;
					}
					res = strncmp(ter_actual, palabra.c_str(), longitud);

					if ((res == 0) && (palabra.size()==tamanio_actual)) {
						encontrado = true;
						this->agregar_doc_rec(indice,consulta,docs,pos_termino);
					}
					k++;
					delete[] ter_anterior;
				}
				piso = medio+1;

			}
			else {
				techo = medio-1;
			}
		}

		if ((encontrado) || (piso> techo)) {
			end = true;
		}

		delete[] ter_actual;
	}

	std::cout << "Termino: " << palabra;
	if (!encontrado) {
		std::cout << " no encontrada en el indice";
		(consulta->encontrado[pos_termino]) = false;
	}
	else {
		std::cout << " encontrada en el indice" << std::endl;
	}

	delete[] buffer;
}


void TablaIndice::agregar_doc_rec(int pos, vector_consulta* consulta, DocumentosRecuperados* docs, int pos_termino) {
	tabla_indice actual = this->tabla[pos];
	tabla_indice siguiente = this->tabla[pos+1];
	Delta d;
	int pos_actual = 0;
	CompresorDePunteros c;
	(consulta->encontrado[pos_termino]) = true;
	std::string cadena =c.descomprimir(actual.offset_punteros, siguiente.offset_punteros, this->path_punteros);
	int i = 0;
	consulta->cant_doc_termino[pos_termino] = actual.frec;
	int doc_anterior = 0;
	while (i < (actual.frec)) {
		int doc = d.descomprimir(cadena, &pos_actual);
		int frec = d.descomprimir(cadena, &pos_actual);
		doc = doc + doc_anterior;
		docs->agregar_doc(doc, frec , pos_termino);
		i++;
		doc_anterior = doc;
	}
}

